package cn.edu.cuit.calculator.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;


public class CalculateUtil {
    /** 数字栈 */
    private static final Stack<BigDecimal> numberStack = new Stack<>();
    /** 符号栈 */
    private static final Stack<Character> symbolStack = new Stack<>();
    /** 将百分号处理成小数 */
    private static final BigDecimal HUNDRED = new BigDecimal(100);
    /** 左括号 */
    private static final String LEFT_BRACKET = "(";
    /** 右括号 */
    private static final String RIGHT_BRACKET = ")";

    public static void main(String[] args) {
        String validate = "7/6";
        System.out.println(execute(validate));
    }

    /**
     * 运行方法
     * @param str   需要计算结果的式子
     * @return  String  返回运算的结果
     */
    public static String execute(String str){
        if(str.length() - str.replaceAll("[0-9]", "").length() == 0){
            throw new RuntimeException("不能全为运算符！");
        }
        String afterComputedStr =  str.contains(LEFT_BRACKET) ?
                getLeastParentheses(matchingParentheses(str)) :
                calculate(str);
        return afterComputedStr;
    }

    /**
     * 匹配括号的数量
     * @param str   要校验的字符串
     * @return  如果左括号和右括号的数量不一致的话添加右括号
     */
    private static String matchingParentheses(String str){
        str = str.replaceAll("()", "");
        int length = str.length();
        int left = length - str.replace("(","").length();
        int right = length - str.replace(")","").length();
        if(left != right){
            for (int i = 0; i < left - right; i++) {
                str += ")";
            }
        }
        return str;
    }

    private static String getLeastParentheses(String str){
        int lastIndex = str.lastIndexOf(LEFT_BRACKET);
        int latestIndex = str.indexOf(RIGHT_BRACKET,lastIndex);
        String subStr = str.substring(lastIndex + 1,latestIndex);
        String afterStr = str.substring(0, lastIndex) + calculate(subStr) + str.substring(latestIndex + 1);
        while(afterStr.contains(LEFT_BRACKET)){
            afterStr = getLeastParentheses(afterStr);
        }
        return calculate(afterStr);
    }

    private static String calculate(String str){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char itemChar = str.charAt(i);
            if(isNumber(itemChar)){
                sb.append(itemChar);
            }
            if(isSymbol(itemChar)){
                boolean minusFlag = itemChar == '-' && (i == 0 || isSymbol(str.charAt(i - 1)));
                if(minusFlag){
                    sb.append("-");
                }else{
                    BigDecimal number = BigDecimal.valueOf(Double.parseDouble(sb.toString()));
                    sb = new StringBuilder();
                    if(itemChar == '%'){
                        number = number.divide(HUNDRED);
                        numberStack.push(number);
                    }else{
                        if(isLower(itemChar)){
                            numberStack.push(computed(symbolStack.pop(), number));
                            symbolStack.push(itemChar);
                        }else{
                            numberStack.push(number);
                            symbolStack.push(itemChar);
                        }
                    }
                }
            }
        }
        String lastStr = sb.toString();
        if(lastStr != null && lastStr.length() != 0){
            numberStack.push(BigDecimal.valueOf(Double.parseDouble(sb.toString())));
        }
        return computed() + "";

    }

    private static BigDecimal computed(){
        for (Iterator iterator = symbolStack.iterator(); iterator.hasNext();){
            char symbol = symbolStack.pop();
            BigDecimal number = numberStack.pop();
            BigDecimal computed = computed(symbol, number);
            numberStack.push(computed);
        }
        return numberStack.pop();
    }

    /**
     * 计算
     * @param symbol    运算符
     * @param number    数字
     * @return
     */
    private static BigDecimal computed(char symbol,BigDecimal number){
        switch (symbol){
            case '*' :
                number = number.multiply(numberStack.pop()) ;
                break;
            case '/' :
                if(number.equals(BigDecimal.ZERO)){
                    throw new RuntimeException("除数不能为0！");
                }
                number = numberStack.pop().divide(number,5, RoundingMode.FLOOR);
                break;
            case '-' :
                number = numberStack.pop().subtract(number);
                break;
            case '+' :
                number = number.add(numberStack.pop());
                break;
            case '.' :
                number = number.divide(BigDecimal.TEN);
                break;
            default :
                break;
        }
        return number;
    }

    /**
     * 比较符号的优先级
     * @param symbol    算式中的运算符
     * @return
     */
    private static boolean isLower(char symbol){
        if(symbolStack.isEmpty()){
            return false;
        }
        Map<Character, Integer> priorityMap = getPriorityMap();
        return priorityMap.get(symbol) >= priorityMap.get(symbolStack.peek());
    }

    private static boolean noSymbol(String str){
        return str.length() - str.replaceAll("[-+/*.%(^)]","").length() == 0;
    }

    private static Map<Character,Integer> getPriorityMap(){
        Map<Character,Integer> map = new HashMap<>(5);
        map.put('%', 0);
        map.put('/', 1);
        map.put('*', 1);
        map.put('-', 2);
        map.put('+', 2);
        return map;
    }

    /**
     * 是否是数字
     * @param itemChar  字符
     * @return
     */
    public static boolean isNumber(char itemChar){
        return itemChar == '.' || itemChar >= '0' && itemChar <= '9';
    }

    /**
     * 是否是符号
     * @param itemChar  符号
     * @return
     */
    public static boolean isSymbol(char itemChar){
        return !isNumber(itemChar);
    }
}